#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define LINE_SIZE 1024
#define LEFT "["
#define RIGHT "]"
#define LABLE "$"
#define DELIM " \t"
#define ARGC_SIZE 32
#define EXIT_CODE 404  // 退出码，自己随便定义的

int quit = 0;
char commandLine[LINE_SIZE]; // 存储输入的命令
char* argv[ARGC_SIZE]; // 存储分解的命令
int lastCode = 0; // 收集最后的退出码
char pwd[LINE_SIZE]; // 记录当前的绝对路径
char myenv[LINE_SIZE]; // 自定义环境变量表

const char* GetUserName()
{
  return getenv("USER");
}

const char* GetHostName()
{
  return getenv("HOSTNAME");
}

void GetPwd()
{
  getcwd(pwd, sizeof(pwd)); // 获取当前目录的绝对路径放入pwd中
}

void Interact(char* cline, int size) // 获取命令行
{
  GetPwd();
  printf(LEFT"%s@%s %s"RIGHT""LABLE" ", GetUserName(), GetHostName(), pwd);
  char* s = fgets(cline, size, stdin);
  assert(s);
  (void)s; // 用一个s，防止报警告
  cline[strlen(cline) - 1] = '\0';
}

int SplitSring(char* cline, char* _argv[]) // 分解命令行
{
  int i = 0;
  _argv[i++] = strtok(cline, DELIM);
  while (_argv[i++] = strtok(NULL, DELIM));
  return i - 1;
}

void NormalExecute(char* _argv[]) // 执行常规命令
{
   pid_t id = fork();
   if (id < 0)
   {
     perror("fork error");
     return;
   }
   else if (id == 0)
   {
      execvp(_argv[0], _argv);
      exit(EXIT_CODE);
   }
   else 
   {
      int status = 0;
      pid_t ret = waitpid(id, &status, 0);
      if (ret == id)
      {
        lastCode = WEXITSTATUS(status);
      }
   }
}

int BulidCommand(char* _argv[], int _argc)
{
  // 处理内建命令
  if (_argc == 2 && strcmp(_argv[0], "cd") == 0)
  {
    chdir(argv[1]);
    GetPwd();
    sprintf(getenv("PWD"), "%s", pwd);
    return 1;   
  }
  else if (_argc == 2 && strcmp(_argv[0], "export") == 0)
  {
    strcpy(myenv, _argv[1]);
    putenv(myenv);
    return 1;
  }
  else if (_argc == 2 && strcmp(_argv[0], "echo") == 0)
  {
    if (strcmp(_argv[1], "$?") == 0) // 查看最近的退出码
    {
      printf("%d\n", lastCode);
      lastCode = 0;
    }
    else if (*_argv[1] == '$') // 查看环境变量
    {
      char* val = getenv(_argv[1] + 1);
      if (val)
        printf("%s\n", val);
    }
    else // 向屏幕输入 
    {
      printf("%s\n", _argv[1]);
    }

    return 1;
  }

  if (strcmp(_argv[0], "ls") == 0) // 特殊处理ls，给ls加上颜色
  {
    _argv[_argc++] = "--color";
    _argv[_argc] = NULL;
  }

  return 0;
}

int main()
{
  while (!quit)
  {
     Interact(commandLine, sizeof(commandLine)); // 获取命令行

     int argc = SplitSring(commandLine, argv); // 分解命令行
     if (argc == 0) continue;

     int n = BulidCommand(argv, argc); // 判断指令是常规命令还是内建命令
      
     if (!n) // 执行常规命令
     {
       NormalExecute(argv);
     }
  }

  return 0;
}
